/*
 * Lenovo Group
 * Copyright (c) 2015-2016 All Rights Reserved.
 * 
 * Project Name:lmrp-base
 * Create Time: 2016年2月3日 下午4:56:24
 */
package com.lenovo.lmrp.base;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.lenovo.sdc.framework.util.AttributeBean;
import com.lenovo.sdc.framework.util.CommonUtil;
import com.lenovo.sdc.framework.util.Tuple;
import com.lenovo.sdc.framework.util.exception.ErrorCode;
import com.lenovo.sdc.framework.util.exception.TraceableException;

/**
 * following parameter should be configured
 * file.server.type=local|remote
 * file.rootpath=data
 * ftp.server.host
 * ftp.server.port=21
 * ftp.server.username
 * ftp.server.password
 * #mode can be localpassive, remotepassive, localactive
 * ftp.server.mode=localactive
 * 
 * @author wujin
 * @version v1.0
 */
public class FileSystemService implements IFileService {
    private static final Logger logger = LoggerFactory.getLogger(FileSystemService.class);
    
    private AttributeBean params;
    
    @Inject
    public FileSystemService(AttributeBean params) {
        this.params = (Objects.isNull(params) ? new AttributeBean() : params);
    }
    
    public Tuple.Pair<String, Long> writeFile(String relativePath, String data) {
        return writeFile(relativePath, data.getBytes());
    }
    
    public Tuple.Pair<String, Long> writeFile(String relativePath, byte[] data) {
        String filePath = makeFilePath(relativePath);
        File file = new File(filePath);
        
        BufferedOutputStream out = null;
        try {
            createDirs(file);
            if (file.exists() == false)
                file.createNewFile();

            out = new BufferedOutputStream(new FileOutputStream(file));
            out.write(data);
            out.flush();
            
            logger.info("save file:{}", file.getAbsolutePath());
        }
        catch (Exception exp) {
            throw new TraceableException(ErrorCode.FILE_ERROR, exp);
        }
        finally {
            CommonUtil.closeIO(out);
        }
        return Tuple.makePair(file.getAbsolutePath(), file.length());
    }
    
    public Tuple.Pair<String, Long> writeFile(String relativePath, InputStream in) {
        String filePath = makeFilePath(relativePath);
        File file = new File(filePath);
        
        BufferedOutputStream out = null;
        try {
            createDirs(file);
            if (file.exists() == false)
                file.createNewFile();
            
            int readLen = 0;
            byte[] buf = new byte[1024];
            out = new BufferedOutputStream(new FileOutputStream(file));
            while((readLen = in.read(buf)) > 0)
                out.write(buf, 0,  readLen);
            
            out.flush();
            
            logger.info("save file:{}", file.getAbsolutePath());
        }
        catch (Exception exp) {
            throw new TraceableException(ErrorCode.FILE_ERROR, exp);
        }
        finally {
            CommonUtil.closeIO(out);
        }
        return Tuple.makePair(file.getAbsolutePath(), file.length());
    }
    
    /**
     * Read file as String, encoder is UTF-8. 
     * So pay attention to if your file is not text file will cause issue.
     * 
     */
    public long readFile(String absolutePath, StringBuilder buf) {
        File file = new File(absolutePath);
        if (file.exists() == false)
            throw new TraceableException(ErrorCode.FILE_NOTFOUND, file.getAbsolutePath());

        Reader in = null;
        try {
            int readLen = 0;
            char[] data = new char[1024];
            in = new InputStreamReader(new FileInputStream(file), "UTF-8");
            while((readLen = in.read(data)) > 0) {
                buf.append(data, 0, readLen);
            }
        }
        catch (Exception exp) {
            throw new TraceableException(ErrorCode.FILE_ERROR, exp);
        }
        finally {
            CommonUtil.closeIO(in);
        }
        return file.length();
    }
    
    public long readFile(String absolutePath, ByteBuffer buf) {
        return 0;
    }
    
    public long readFile(String absolutePath, OutputStream out) {
        File file = new File(absolutePath);
        if (file.exists() == false)
            throw new TraceableException(ErrorCode.FILE_NOTFOUND, file.getAbsolutePath());
        
        BufferedInputStream in = null;
        try {
            int readLen = 0;
            byte[] data = new byte[1024];
            in = new BufferedInputStream(new FileInputStream(file));
            while((readLen = in.read(data)) > 0) {
                out.write(data, 0, readLen);
            }
        }
        catch (Exception exp) {
            throw new TraceableException(ErrorCode.FILE_ERROR, exp);
        }
        finally {
            CommonUtil.closeIO(in);
        }
        return file.length();
    }
    
    private String makeFilePath(String relativePath) {
        String path = relativePath.replace('\\', '/');
        String root = params.getAttribute(ParamNameDef.FILE_ROOT_PATH, "data");
        if (path.startsWith("/")) {
            if (root.endsWith("/"))
                return root + path.substring(1);
            else
                return root + path;
        }
        else {
            if (root.endsWith("/"))
                return params.getAttribute(ParamNameDef.FILE_ROOT_PATH, "data") + path;
            else
                return params.getAttribute(ParamNameDef.FILE_ROOT_PATH, "data") + "/" + path;
        }
    }
    
    private boolean createDirs(File file) {
        File dir = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(File.separatorChar)));
        logger.info("create directory:{}", dir.getAbsolutePath());
        return dir.mkdirs();
    }
}
